/**
 * Copyright (c) 2006
 *      OpenAVS Developers. All Rights Reserved.
 *
 * Copyright (c) 2005-2006
 *      NSCC. All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

/**
 * ! \file "decode.c"
 *   \brief Frame decoding API
 */

#include "vlc.h"
#include "block.h"
#include "macroblock.h"
#include "loopfilter.h"
#include "image.h"
#include "global.h"
#include "stream.h"
#include <time.h>
#include <sys/timeb.h>
#include "endian.h"

static AVS_DWORD PictureData(
		AVS_BYTE*   pbData,
		AVS_DWORD         dwFrameLen,
		AVS_DWORD*        pdwBitOffset,
		STREAMINFO*       StrmInfo,
		VIDEODATA**       ppRefFrame, 
		VIDEODATA*        pCurrFrame, 
		MBINFO*           pMbInfo,
		BWREFINFO*        pBwRefInfo);


AVS_HRESULT DecodeOneFrame(AVS_BYTE * pbData,
		AVS_DWORD dwFrameLen,
		STREAMINFO * StrmInfo, 
		VIDEODATA ** ppRefFrame,
		VIDEODATA ** pCurrFrame, 
		MBINFO * pMbInfo,
		BWREFINFO * pBwRefInfo)
{
	AVS_BYTE * pbCurrent = pbData; 
	AVS_DWORD dwCode = *(AVS_DWORD *) pbCurrent;
	AVS_DWORD dwBitOffset = 0;

	if ((DWORD_SWAP(dwCode) != I_FRAME_START_CODE) && 
	    (DWORD_SWAP(dwCode) != PB_FRAME_START_CODE))
		return AVS_NOT_VALID_PIC_DATA;

	GetImgHeaderInfo(pbCurrent, dwFrameLen, &dwBitOffset, StrmInfo);

	// AVS_DWORD imgWid = StrmInfo->SeqInfo.dwWidth;
	// AVS_DWORD imgHei = StrmInfo->SeqInfo.dwHeight;

	StrmInfo->ImgInfo.dwImageType1 = StrmInfo->ImgInfo.dwImageType;

	if(StrmInfo->ImgInfo.bPictureStructure)
	{
		// frame type decoding...
		(*pCurrFrame)->dwDistanceIndex = StrmInfo->ImgInfo.dwPictureDistance * 2;
		PictureData(pbCurrent, 
				dwFrameLen,
				&dwBitOffset,
				StrmInfo, 
				ppRefFrame,
				*pCurrFrame, 
				pMbInfo, 
				pBwRefInfo);
	}
	else
	{
		return THIS_VERSION_NOT_SUPPORT;
	}

	if(StrmInfo->ImgInfo.dwImageType != B_IMG)
	{
		UpdateRefBuffer(&ppRefFrame, &pCurrFrame, 2);
	}

	return AVS_NOERROR;   
}

/**
 * Function: Initialize decoer.
 * #- Initialize VLC table
 * #- Initialize Clip table
 * #- Initialize TabBuf (Replace the process of evaluating ABS while decoding)
 * #- Analyze sequence header
 */
AVS_HRESULT InitDecode(const AVS_BYTE* pbData, AVS_DWORD dwDataLen, STREAMINFO* strmInfo)
{
	const AVS_BYTE* pbCurrent = pbData;
	AVS_DWORD dwLeft = dwDataLen;

	MakeVlcTable();
	MakeClipTable();
	MakeTabNoBuf();

	while (dwLeft > 18) {
		AVS_DWORD dwCode = *(AVS_DWORD *) pbCurrent;
		if (
#if __BYTE_ORDER == __LITTLE_ENDIAN
		    ((dwCode & 0x00FFFFFF) == 0x00010000)
#elif __BYTE_ORDER == __BIG_ENDIAN
#else
		    ((dwCode & 0xFFFFFF00) == 0x00000100)
#error "Cannot determine byte order."
#endif
		) {
			dwCode = DWORD_SWAP(dwCode);  
			if(dwCode == SEQENCE_START_CODE) {
				AVS_DWORD dwHeadLen = SeqenceHeader(pbCurrent, dwLeft, &(strmInfo->SeqInfo));
				pbCurrent += dwHeadLen;
				dwLeft -= dwHeadLen;
				break;
			}
		}
		pbCurrent++;
		dwLeft--;
	}
	return dwLeft;
}

static AVS_DWORD PictureData(AVS_BYTE*   pbData,
		AVS_DWORD         dwFrameLen,
		AVS_DWORD*        pdwBitOffset,
		STREAMINFO*   StrmInfo,
		VIDEODATA**   ppRefFrame, 
		VIDEODATA*    pCurrFrame, 
		MBINFO*       pMbInfo,
		BWREFINFO*    pBwRefInfo)
{
	AVS_BYTE * pbCurrent = pbData;
	AVS_DWORD dwLeft = dwFrameLen;
	AVS_DWORD MbIndex = 0;
	AVS_DWORD MbWidth = StrmInfo->SeqInfo.dwMbWidth;
	AVS_DWORD MbHeight = StrmInfo->SeqInfo.dwMbHeight;
	AVS_DWORD MbNum = MbHeight * MbWidth;
	AVS_INT cod_counter = -1;

	AVS_DWORD dwImgWidth = StrmInfo->SeqInfo.dwWidth;
	AVS_SHORT LumaCoef[256];
	AVS_SHORT ChromaCoef[256];

	AVS_DWORD dwCurrDistanceIndex = pCurrFrame->dwDistanceIndex;

	int ii;
	for (MbIndex = 0; MbIndex < MbNum; MbIndex++) { 
		memset(&pMbInfo[MbIndex], 0, sizeof(MBINFO));
		memset(LumaCoef, 0, sizeof(AVS_SHORT) * 256);
		memset(ChromaCoef, 0, sizeof(AVS_SHORT) * 256);

		if (MbIndex % MbWidth == 0) {
			int offset = (((*pdwBitOffset+7) >> 3) << 3);
			while(!IsSliceHeader(pbCurrent, offset)) {

				offset += 8;
				if (offset > 20 * 8)
					break;
			}
			if (IsSliceHeader(pbCurrent, offset)) { 
				*pdwBitOffset = offset;                          
				*pdwBitOffset += 32;
				SliceHeader(pbCurrent, dwLeft, MbIndex, pdwBitOffset, StrmInfo);
			}
		}
		ParseOneMacroBlock(
				&pbCurrent, 
				dwLeft, 
				pdwBitOffset,
				StrmInfo, 
				pMbInfo, 
				MbIndex,
				&cod_counter,
				LumaCoef,
				ChromaCoef);  

		McIdctRecOneMarcroBlock(
				pMbInfo, 
				MbIndex, 
				StrmInfo, 
				(const VIDEODATA **) ppRefFrame, 
				pCurrFrame, 
				2,
				MbWidth,
				MbHeight,
				dwCurrDistanceIndex,
				pBwRefInfo,
				LumaCoef,
				ChromaCoef);  
	}
	for (MbIndex = 0; MbIndex < MbNum; MbIndex++) {
		DeblockOneMacroBlock(pMbInfo, 
				MbIndex,
				dwImgWidth >> 4,
				StrmInfo,
				pCurrFrame
				);
	}   

	return AVS_NOERROR;
}

